/*
 Name:    STE_ESPShooting.ino
 Created: 2020/11/4 14:31:41
 Author:  t1019
*/
#include <WiFiServer.h>
#include <WiFiClientSecure.h>
#include <WiFiClient.h>
#include <ESP8266WiFiType.h>
#include <ESP8266WiFiSTA.h>
#include <ESP8266WiFiScan.h>
#include <ESP8266WiFiGeneric.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <malloc.h>
#include <EEPROM.h>

using namespace std;
// the setup function runs once when you press reset or power the board
WiFiUDP Udp;
IPAddress remote_ip(192, 168, 31, 254);
IPAddress broadcast_ip(192, 168, 31, 255);
uint16_t local_port = 8002;
uint16_t remote_port = 8001;
bool connected = false;
const int QUEUE_SIZE = 20;
const int FIFO_SIZE = 100;
const int LED_PIN = 16;
const int PERIOD_TIMEOUT = 5;
const int DEBUG_MODE = 0; //0——关闭注释 1——打开注释


//Mr_Space
#define EEPROM_addr 1
char ssid[7] = "STE-01";
char pswd[9] = "01010101";
#define READY_PIN 2

//数据包类型
typedef union
{
    struct
    {
        uint8_t header;
        uint8_t type;
        uint8_t addr[2];
        uint8_t laddr[2];
        uint8_t datacode;
        uint8_t data[2];
        uint8_t sum;
    }data;
    uint8_t buf[10];
}UdpPackage;

//缓冲区类型
struct DataQueue
{
    UdpPackage queue[QUEUE_SIZE];
    int index;                      //队列里有多少元素
    bool isEmpty()                  //队空
    {
        return (index == 0);
    }
    bool enqueue(UdpPackage data)   //入队
    {
        if (index == QUEUE_SIZE)
            return false;
        else
        {
            queue[index] = data;
            index++;
            return true;
        }
    }
    UdpPackage dequeue()            //出队
    {
        if (index == 0)
        {
            UdpPackage n;
            return n;
        }
        else
        {
            index--;
            return queue[index];
        }
    }
}SerialQueue, WifiQueue;

struct
{
    int index;                      //索引变量
    UdpPackage Rec;                 //当前接受到的数据包
    struct DataQueue* queue;        //发送队列-接受到的完整数据包会入队
    int data_size;                  //缓冲区数据尺寸
    uint8_t rec_buf[FIFO_SIZE];     //接收缓冲区
    void init(struct DataQueue* q) 
    {
        index = 0;
        queue = q;
        data_size = 0;
    }
    uint8_t cal_sum()                   //计算校验和
    {
        uint8_t sum = 0;
        for (int i = 0; i < 9; i++)
            sum += Rec.buf[i];
        return sum;
    }
    void data_handler(uint8_t ch)       //消息校验
    {
        if (index == 0 && (ch == 0xB1 || ch == 0xB2 || ch == 0xB3))
        {
            Rec.buf[0] = ch;
            index++;
        }
        else if (index >= 1 && index <= 8)
        {
            Rec.buf[index] = ch;
            index++;
        }
        else if (index == 9)
        {
            Rec.buf[index] = ch;
            if (ch == cal_sum())
            {
                //检测Rec是否是配置帧如果不是则入队
                if(1)
                    queue->enqueue(Rec);    //若校验成功则入队
                
            }
            index = 0;
        }
        else
            index = 0;
    }
    void process()                      //数据处理
    {
        for (int i = 0; i < data_size; i++)
            data_handler(rec_buf[i]);
        data_size = 0;
    }
}SerialRec,WifiRec;

enum LED_MODE   //LED模式
{
    LED_OFF = 0,
    LED_ON = 1,
    LED_SLOW = 2,
    LED_FAST = 3,
    LED_BLINK = 4
};

struct              //LED亮灯的一个结构
{
    int pin;
    int time_cnt;
    enum LED_MODE mode;
    void init(int pin_num)
    {
        pin = pin_num;
        mode = LED_OFF;
        time_cnt = 0;
        pinMode(pin_num, OUTPUT);
        digitalWrite(pin, HIGH);
    }
    void handler(int time)
    {
        time_cnt += time;
        switch (mode)
        {
        case LED_OFF:
            digitalWrite(pin, HIGH);
            time_cnt = 0;
            break;
        case LED_ON:
            digitalWrite(pin, LOW);
            time_cnt = 0;
            break;
        case LED_SLOW:
            if(time_cnt>500)
                digitalWrite(pin, LOW);
            else
                digitalWrite(pin, HIGH);
            if (time_cnt > 1000)
                time_cnt = 0;
            break;
        case LED_FAST:
            if (time_cnt > 200)
                digitalWrite(pin, LOW);
            else
                digitalWrite(pin, HIGH);
            if (time_cnt > 500)
                time_cnt = 0;
            break;
        case LED_BLINK:
            if (time_cnt > 200)
                digitalWrite(pin, LOW);
            else
                digitalWrite(pin, HIGH);
            if (time_cnt > 2000)
                time_cnt = 0;
            break;
        }
    }
}Led;

//WiFiID初始化，从EEPROM中读取NET_ID
//Mr_Space
void WiFiID_INIT()
{
    char C_1, C_2;
    EEPROM.begin(4);
    C_1 = EEPROM.read(EEPROM_addr);
    C_2 = EEPROM.read(EEPROM_addr+1);
    ssid[4]=C_1;
    ssid[5]=C_2;
    pswd[0]=pswd[2]=pswd[4]=pswd[6]=C_1;
    pswd[1]=pswd[3]=pswd[5]=pswd[7]=C_2;
    if(DEBUG_MODE)
    {
        Serial.println(ssid);
        Serial.println(pswd);
        Serial.println("End read");
    }
}
//WiFiID更新，将NWT_ID写入EEPROM，并更新ssid和pswd
void WiFiID_WRITE(char C_1, char C_2)
{
    EEPROM.begin(4);
    EEPROM.write(EEPROM_addr,   C_1);
    EEPROM.write(EEPROM_addr+1, C_2);
    EEPROM.end();
    if(DEBUG_MODE)
        Serial.println("End write");
    WiFiID_INIT();
}

void setup() {
    Serial.begin(115200);
    WiFi.mode(WIFI_STA);

    if(DEBUG_MODE)
    {
        WiFiID_WRITE('0','1');
        Serial.println("\nStart");
    }
    //TODO1：EEPROM回读SSID PSWD
    WiFiID_INIT();
    
    WiFi.begin(ssid,pswd);
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(2000);
        if(DEBUG_MODE)
            Serial.println("Reconnected");
    }

    if(DEBUG_MODE)
    {
        Serial.println("Connected");
        Serial.println(WiFi.localIP());
    }
    
    WiFi.setAutoReconnect(true);
    Udp.begin(local_port);
    Led.init(LED_PIN);
    SerialRec.init(&WifiQueue);     //串口接收的送到wifi发送队列
    WifiRec.init(&SerialQueue);     //wifi接收的数据送到串口队列
    
    //TODO2：把READY_PIN引脚拉高/拉低
    /*
    pinMode(READY_PIN, OUTPUT);
    delay(PERIOD_TIMEOUT);
    digitalWrite(READY_PIN, HIGH);*/
    
    
}

// the loop function runs over and over again until power down or reset
void loop() {
    int recSize = 0;
    UdpPackage WifiTx;
    //Step1:串口接收全部数据并解析-MCU发给ESP的数据
    recSize = Serial.available();
    if (recSize)
    {
        SerialRec.data_size = Serial.readBytes(SerialRec.rec_buf, recSize);
        SerialRec.process();//每次执行process就会把data_size个数据处理掉并清零
    }
    //Step2:串口发送消息-把缓存的UDP接收到的消息用串口转发给MCU
    if (!SerialQueue.isEmpty())
    {
        Serial.write(SerialQueue.dequeue().buf, 10);
    }
    //Step3:UDP接收数据并缓存
    recSize = Udp.parsePacket();
    if (recSize)
    {
        WifiRec.data_size = Udp.read(WifiRec.rec_buf, FIFO_SIZE);
        WifiRec.process();
    }
    //STEP4：UDP发送消息
    //TODO（优先级不高）:检查信号强度RSSI，如果信号弱且有同名的高强度热点，就连新的。
    if (WiFi.isConnected()) //检查是否连接热点
    {
        Led.mode = LED_SLOW;
        if (!WifiQueue.isEmpty())
        {
            WifiTx = WifiQueue.dequeue();
            if(WifiTx.data.header == 0xB2 
                || WifiTx.data.header == 0xB3) {
                  Udp.beginPacket(broadcast_ip, local_port);
                  Udp.write(WifiTx.buf,10);
                  Udp.endPacket();
                }
                
            
                Udp.beginPacket(remote_ip, remote_port);
            Udp.write(WifiTx.buf,10);
            Udp.endPacket();
        }
    }
    else
      Led.mode = LED_OFF;
    Led.handler(PERIOD_TIMEOUT);
    delay(PERIOD_TIMEOUT);
    //Serial.print("loop once.\n");
}
